/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package yagui4ctte;

import edu.uci.ics.jung.algorithms.layout.CircleLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.graph.DirectedSparseMultigraph;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.visualization.DefaultVisualizationModel;
import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.AbstractModalGraphMouse;
import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.control.EditingModalGraphMouse;
import edu.uci.ics.jung.visualization.control.GraphMouseListener;
import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
import edu.uci.ics.jung.visualization.control.ModalGraphMouse.Mode;
import edu.uci.ics.jung.visualization.control.ScalingControl;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import edu.uci.ics.jung.visualization.renderers.Renderer.VertexLabel.Position;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collection;
import java.util.Iterator;
import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import org.apache.commons.collections15.Factory;
import org.apache.commons.collections15.Transformer;


/**
 *
 * @author hugo
 */
public class Arbre extends JPanel{
    
    private Graph<Node, Link> g;
    private VisualizationViewer<Node, Link> vv;
    private Layout<Node, Link> layout;
    private ScalingControl scaler;
    private AbstractModalGraphMouse gm;
    private JRadioButton btnMousePick = new JRadioButton("Selection");
    private JRadioButton btnMouseTrans = new JRadioButton("Transformation");
    private JRadioButton btnMouseEdit = new JRadioButton("Ajout/Suppression");
    
    private JButton ajoutNode = new JButton(new ImageIcon("src/yagui4ctte/plus.png"));
    private JButton supprimerNode = new JButton(new ImageIcon("src/yagui4ctte/moins.png"));
    private JButton editerNode = new JButton(new ImageIcon("src/yagui4ctte/edit.png"));

    private JPanel hbtns = new JPanel();
    private JPanel vbtns = new JPanel(new GridBagLayout());

    private GraphMouseListener gel;
    private MouseListener ml;
    private Node selNode;
    private Link selLink;
    private Gui gui;


    public Arbre(Gui gui){
        this.gui = gui;
        g = new DirectedSparseMultigraph<Node, Link>();
        scaler = new CrossoverScalingControl();
        this.setLayout(new BorderLayout());
        ButtonGroup group = new ButtonGroup();

        group.add(btnMousePick);
        group.add(btnMouseTrans);
        group.add(btnMouseEdit);
        btnMousePick.setToolTipText("Cet outil vous aideras à sélectionner ou déplacer les neuds individuellement");
        btnMouseTrans.setToolTipText("Cet outil vous permettras de modifier l'allur de votre arbre (essayez les touches Ctrl et Shift)");
        btnMouseEdit.setToolTipText("Cet outil vous permet d'ajouter et de supprimer les neuds directement en cliquant dessus");

        btnMouseEdit.setSelected(true);

        hbtns.add(btnMousePick);
        hbtns.add(btnMouseTrans);
        hbtns.add(btnMouseEdit);

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = GridBagConstraints.RELATIVE;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.insets = new Insets(10, 0, 10, 0);
        
        vbtns.add(ajoutNode,gbc);
        vbtns.add(supprimerNode,gbc);
        vbtns.add(editerNode,gbc);
        ajoutNode.setToolTipText("Ajouter un noeud (vous pouvez sélectionner sa racine)");
        supprimerNode.setToolTipText("Supprimer le noeud sélectionné et ses enfants");
        editerNode.setToolTipText("Editer le noeud sélectionné");
        ajoutNode.setEnabled(true);
        supprimerNode.setEnabled(false);
        editerNode.setEnabled(false);

        ActionListener al = new ActionListener(){
            public void actionPerformed(ActionEvent arg0) {

                if(arg0.getSource().equals(btnMousePick)){
                    mouseMode(ModalGraphMouse.Mode.PICKING);
                }
                if(arg0.getSource().equals(btnMouseTrans)){
                    mouseMode(ModalGraphMouse.Mode.TRANSFORMING);
                }
                if(arg0.getSource().equals(btnMouseEdit)){
                    mouseMode(ModalGraphMouse.Mode.EDITING);
                }
                
                if(arg0.getSource().equals(ajoutNode)){
                    CustomDialog c = new CustomDialog(null,new Node());
                    if(!vv.getPickedVertexState().isPicked(selNode)){
                        g.addVertex(c.getNode());
                    }else{
                        g.addEdge(new Link(), selNode, c.getNode());
                    }
                }
                
                if(vv.getPickedVertexState().isPicked(selNode)){
                    if(arg0.getSource().equals(supprimerNode)){
                        Collection<Node> c = g.getSuccessors(selNode);
                        for(Node objet : c){
                            g.removeVertex(objet);
                        }
                        g.removeVertex(selNode);
                        supprimerNode.setEnabled(false);
                        ajoutNode.setEnabled(true);
                        editerNode.setEnabled(false);
                    }
                    if(arg0.getSource().equals(editerNode)){
                        CustomDialog c = new CustomDialog(null,selNode);
                        Collection<Node> prec = g.getPredecessors(selNode);
                        Collection<Node> suiv = g.getSuccessors(selNode);
                        g.removeVertex(selNode);
                        Node n = c.getNode();
                        g.addVertex(n);
                        Iterator<Node> precit = prec.iterator();
                        if(precit.hasNext())
                            g.addEdge(new Link(), precit.next(), n);
                        Iterator<Node> suivit = suiv.iterator();
                        if(suivit.hasNext())
                            g.addEdge(new Link(), n, suivit.next());
                    }
                }else{
                    supprimerNode.setEnabled(false);
                    ajoutNode.setEnabled(true);
                    editerNode.setEnabled(false);
                }
                addvv();
            }
        };

        gel = new GraphMouseListener(){

            public void graphClicked(Object arg0, MouseEvent arg1) {
                if(arg0 instanceof Node) selNode=(Node)arg0;
                if(arg0 instanceof Link) selLink=(Link)arg0;
                ajoutNode.setEnabled(true);
                supprimerNode.setEnabled(true);
                editerNode.setEnabled(true);
            }

            public void graphPressed(Object arg0, MouseEvent arg1) {
            }

            public void graphReleased(Object arg0, MouseEvent arg1) {
            }

        };

        ml = new MouseListener(){

            public void mouseClicked(MouseEvent arg0) {
                if(!vv.getPickedVertexState().isPicked(selNode)){
                    supprimerNode.setEnabled(false);
                    ajoutNode.setEnabled(true);
                    editerNode.setEnabled(false);
                }else{
                    ajoutNode.setEnabled(true);
                    supprimerNode.setEnabled(true);
                    editerNode.setEnabled(true);
                }
            }

            public void mousePressed(MouseEvent arg0) {

            }

            public void mouseReleased(MouseEvent arg0) {

            }

            public void mouseEntered(MouseEvent arg0) {
                if(arg0.getSource() instanceof JButton){
                    setStatusLabel(((JButton)arg0.getSource()).getToolTipText());
                }else if(arg0.getSource() instanceof JRadioButton){
                    setStatusLabel(((JRadioButton)arg0.getSource()).getToolTipText());
                }
            }

            public void mouseExited(MouseEvent arg0) {
                setStatusLabel("Bienvenue dans Yagui4CTTE");
            }


        };

        btnMousePick.addActionListener(al);
        btnMouseTrans.addActionListener(al);
        btnMouseEdit.addActionListener(al);
        ajoutNode.addActionListener(al);
        supprimerNode.addActionListener(al);
        editerNode.addActionListener(al);

        btnMousePick.addMouseListener(ml);
        btnMouseTrans.addMouseListener(ml);
        btnMouseEdit.addMouseListener(ml);
        ajoutNode.addMouseListener(ml);
        supprimerNode.addMouseListener(ml);
        editerNode.addMouseListener(ml);

        this.add(hbtns,BorderLayout.SOUTH);
        this.add(vbtns,BorderLayout.WEST);   
        this.updateGraph();
        this.mouseMode(ModalGraphMouse.Mode.EDITING);
    }

    public void setStatusLabel(String s){
        gui.setStatusLabel(s);
    }

    public void updateGraph(){
        layout = new CircleLayout(g);
        DefaultVisualizationModel vm = new DefaultVisualizationModel<Node,Link>(layout);
        vv = new VisualizationViewer<Node, Link>(vm);
        vv.addGraphMouseListener(gel);
        vv.addMouseListener(ml);
        vv.setBackground(Color.white);
        //ScrollPanes et zoom à la molette
        vv.setLayout(new BorderLayout());

        //Gestion du zoom
        vv.scaleToLayout(scaler);
        
        //Labels
        vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
        vv.getRenderer().getVertexLabelRenderer().setPosition(Position.CNTR);
        vv.setVertexToolTipTransformer(new VertexLabeller());
        vv.getRenderContext().setVertexIconTransformer(new Transformer<Node,Icon>() {
            public Icon transform(Node n) {
                Icon i = new ImageIcon();
                if(n.getCategorie().equals(Node.categorie.Abstraction)){i = new ImageIcon("src/yagui4ctte/cloud.png");}
                else if(n.getCategorie().equals(Node.categorie.Application)){i = new ImageIcon("src/yagui4ctte/app.png");}
                else if(n.getCategorie().equals(Node.categorie.Interaction)){i = new ImageIcon("src/yagui4ctte/int.png");}
                else if(n.getCategorie().equals(Node.categorie.Utilisateur)){i = new ImageIcon("src/yagui4ctte/user.png");}
                return i;
            }
        });
        addvv();
    }

    public void addvv(){
        this.add(new GraphZoomScrollPane(vv),BorderLayout.CENTER);
        this.revalidate();
    }

    public void mouseMode(Mode mode){
        if (mode.equals(ModalGraphMouse.Mode.EDITING)){
            //Editer le graph à l'aide de la souris
            Factory<Node> vertexFactory = new VertexFactory();
            Factory<Link> edgeFactory = new EdgeFactory();
            gm = new EditingModalGraphMouse<Node, Link>(vv.getRenderContext(), vertexFactory, edgeFactory);
            vv.setGraphMouse(gm);
        }else if(mode.equals(ModalGraphMouse.Mode.TRANSFORMING)
                || mode.equals(ModalGraphMouse.Mode.PICKING)){
            gm = new DefaultModalGraphMouse<Node, Link>();
            vv.setGraphMouse(gm);
        }
        gm.setMode(mode);

    }


    public void reduit(){
        scaler.scale(vv, 1.1f, vv.getCenter());
    }
    
    public void agrandit(){
        scaler.scale(vv, 1.1f, vv.getCenter());
    }

}
class VertexFactory implements Factory<Node> {
    public Node create() {
        CustomDialog c = new CustomDialog(null,new Node());
        return c.getNode();
    }
}

class EdgeFactory implements Factory<Link> {
    public Link create() {
        return new Link();
    }
}


class VertexLabeller implements Transformer{
    public Object transform(Object arg0) {
     String s = new String();
        if(arg0 instanceof Node){
        Node n = (Node)arg0;
        s = "<html>"+
        "<b>"+n.getId()+"</b>"+
        "<p>Catégorie : "+n.getCategorie().name()+
        "<p>Fréquence : "+n.getFrequence().name()+
        "<p>Plateforme : "+n.getPlateForme().name()+
        "</html>";
        }
     return s;
    }
}
class CustomDialog extends JDialog implements PropertyChangeListener {

    private JOptionPane optionPane;

    private String btnString1 = "Créer";
    private String btnString2 = "Annuler";

    private Node n;
    private JTextField nomTF = new JTextField(10);
    private JRadioButton btnAbstraction = new JRadioButton("Abstraction");
    private JRadioButton btnApplication = new JRadioButton("Application");
    private JRadioButton btnInteraction = new JRadioButton("Interaction");
    private JRadioButton btnUtilisateur = new JRadioButton("Utilisateur");
    private JRadioButton btnBasse = new JRadioButton("Basse");
    private JRadioButton btnHaute = new JRadioButton("Haute");
    private JRadioButton btnMoyenne = new JRadioButton("Moyenne");
    private JRadioButton btnPda = new JRadioButton("Pda");
    private JRadioButton btnBureau = new JRadioButton("Bureau");
    private JRadioButton btnMobile = new JRadioButton("Mobile");
    private JRadioButton btnVocal = new JRadioButton("Vocal");
    private JButton ok = new JButton("Créer");

    /** Creates the reusable dialog. */
    public CustomDialog(Frame aFrame, Node n) {

        super(aFrame, true);
        this.n = n;
        this.setPreferredSize(new Dimension(400,400));
        this.setMinimumSize(new Dimension(400,400));
        setTitle("Propriétés");

        JPanel jp = new JPanel(new GridLayout(5,1));
        JPanel jp1 = new JPanel();
        JPanel jp2 = new JPanel();
        JPanel jp3 = new JPanel();
        JPanel jp4 = new JPanel();
        jp.add(jp1);
        jp.add(jp2);
        jp.add(jp3);
        jp.add(jp4);
        JPanel jp5 = new JPanel();
        jp5.add(ok);
        jp.add(jp5);
        jp1.add(new JLabel("Nom : "));
        
        jp1.add(nomTF);
        //Catégorie
        jp2.add(new JLabel("Catégorie : "));
        ButtonGroup groupCat = new ButtonGroup();

        btnAbstraction.setSelected(true);
        groupCat.add(btnAbstraction);
        groupCat.add(btnApplication);
        groupCat.add(btnInteraction);
        groupCat.add(btnUtilisateur);
        jp2.add(btnAbstraction);
        jp2.add(btnApplication);
        jp2.add(btnInteraction);
        jp2.add(btnUtilisateur);
        //Fréquence
        jp3.add(new JLabel("Fréquence : "));
        ButtonGroup groupFreq = new ButtonGroup();
        
        btnBasse.setSelected(true);
        groupFreq.add(btnBasse);
        groupFreq.add(btnHaute);
        groupFreq.add(btnMoyenne);
        jp3.add(btnBasse);
        jp3.add(btnMoyenne);
        jp3.add(btnHaute);
        //Plateforme
        jp4.add(new JLabel("Plateforme : "));
        ButtonGroup groupPlt = new ButtonGroup();
       
        btnPda.setSelected(true);
        groupPlt.add(btnPda);
        groupPlt.add(btnBureau);
        groupPlt.add(btnMobile);
        groupPlt.add(btnVocal);
        jp4.add(btnPda);
        jp4.add(btnBureau);
        jp4.add(btnMobile);
        jp4.add(btnVocal);

        nomTF.setText(n.getId());
        if(n.getCategorie().equals(Node.categorie.Abstraction)) btnAbstraction.setSelected(true);
        if(n.getCategorie().equals(Node.categorie.Application)) btnApplication.setSelected(true);
        if(n.getCategorie().equals(Node.categorie.Interaction)) btnInteraction.setSelected(true);
        if(n.getCategorie().equals(Node.categorie.Utilisateur)) btnUtilisateur.setSelected(true);
        if(n.getFrequence().equals(Node.frequence.Basse)) btnBasse.setSelected(true);
        if(n.getFrequence().equals(Node.frequence.Moyenne)) btnMoyenne.setSelected(true);
        if(n.getFrequence().equals(Node.frequence.Haute)) btnHaute.setSelected(true);
        if(n.getPlateForme().equals(Node.plateforme.Bureau)) btnBureau.setSelected(true);
        if(n.getPlateForme().equals(Node.plateforme.Pda)) btnPda.setSelected(true);
        if(n.getPlateForme().equals(Node.plateforme.Mobile)) btnMobile.setSelected(true);
        if(n.getPlateForme().equals(Node.plateforme.Vocale)) btnVocal.setSelected(true);

        ok.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent arg0) {
                clearAndHide();
            }
        });
        //Create an array of the text and components to be displayed.
        Object[] array = {jp};

        //Create an array specifying the number of dialog buttons
        //and their text.
        Object[] options = {};

        //Create the JOptionPane.
        optionPane = new JOptionPane(array,
                                    JOptionPane.QUESTION_MESSAGE,
                                    JOptionPane.YES_NO_OPTION,
                                    null,
                                    options);

        //Make this dialog display it.
        setContentPane(optionPane);
        optionPane.setVisible(true);
        setVisible(true);

        //Handle window closing correctly.
        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosing(WindowEvent we) {
                    optionPane.setValue(JOptionPane.CLOSED_OPTION);
            }
        });

        //Ensure the text field always gets the first focus.
        addComponentListener(new ComponentAdapter() {
            @Override
            public void componentShown(ComponentEvent ce) {
            }
        });

        //Register an event handler that reacts to option pane state changes.
        optionPane.addPropertyChangeListener(this);
        this.pack();
    }

    /** This method reacts to state changes in the option pane. */
    public void propertyChange(PropertyChangeEvent e) {
        if (isVisible()
         && (e.getSource() == optionPane)) {
            Object value = optionPane.getValue();

            if (btnString1.equals(value)) {
                clearAndHide();
            }
        }
    }

    public Node getNode(){
        n.setId(this.nomTF.getText());
        if (this.btnAbstraction.isSelected()) n.setCategorie(Node.categorie.Abstraction);
        if (this.btnApplication.isSelected()) n.setCategorie(Node.categorie.Application);
        if (this.btnInteraction.isSelected()) n.setCategorie(Node.categorie.Interaction);
        if (this.btnUtilisateur.isSelected()) n.setCategorie(Node.categorie.Utilisateur);
        if (this.btnBasse.isSelected()) n.setFrequence(Node.frequence.Basse);
        if (this.btnHaute.isSelected()) n.setFrequence(Node.frequence.Haute);
        if (this.btnMoyenne.isSelected()) n.setFrequence(Node.frequence.Moyenne);
        if (this.btnBureau.isSelected()) n.setPlateForme(Node.plateforme.Bureau);
        if (this.btnPda.isSelected()) n.setPlateForme(Node.plateforme.Pda);
        if (this.btnVocal.isSelected()) n.setPlateForme(Node.plateforme.Vocale);
        if (this.btnMobile.isSelected()) n.setPlateForme(Node.plateforme.Mobile);
        return n;
    }

    /** This method clears the dialog and hides it. */
    public void clearAndHide() {
        setVisible(false);
    }
}

